package joat.application.automatons;

import java.util.ArrayList;
import java.util.List;

import joat.application.misc.Constants;
import joat.application.ui.ConversationListAdapter;
import joat.application.ui.ConversationListEntry;
import joat.application.ui.R;
import society.framework.android.ActivityAutomaton;
import society.framework.android.SocietyActivity;
import society.framework.core.SocietyInformation;
import society.framework.core.communication.Message;
import society.framework.core.communication.MessageRouter;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.speech.RecognizerIntent;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Toast;

public class UIManagerAutomaton extends ActivityAutomaton {
	protected LinearLayout toolbarLayout;
	protected EditText inputEditText;
	protected ImageView voiceRecognitionButton, sendButton;
	protected ListView conversationList;
	protected ArrayList<ConversationListEntry> conversationListData;
	
	protected static final String INITIALIZE_UI_STATE = "INITIALIZE_UI_STATE";
	protected static final String RESIZE_UI_STATE = "RESIZE_UI_STATE";
	protected static final String IDLE_STATE = "IDLE_STATE";
	protected static final String REQUEST_VOICE_INPUT_STATE = "REQUEST_VOICE_INPUT_STATE";
	protected static final String PROCESS_VOICE_INPUT_RESPONSE_STATE = "PROCESS_VOICE_INPUT_RESPONSE_STATE";
	protected static final String DISPLAY_ERROR_STATE = "DISPLAY_ERROR_STATE";
	protected static final String PROCESS_USER_MESSAGE = "PROCESS_USER_MESSAGE";
	protected static final String PROCESS_OTHER_MESSAGE = "PROCESS_OTHER_MESSAGE";
	protected static final String PROCESS_AUTOMATON_SEND_REQUEST = "PROCESS_AUTOMATON_SEND_REQUEST";
	protected static final String PROCESS_AUTOMATON_REQUEST_REQUEST = "PROCESS_AUTOMATON_REQUEST_REQUEST";

	public UIManagerAutomaton(final Activity activity) {
		super(activity);
		currentStateName = INITIALIZE_UI_STATE;
		
		
		/* 0) THE UI IS INITIALIZED AND THE REFERENCES TO THE UI COMPONENTS ARE INITIALIZED */
		states.put(INITIALIZE_UI_STATE, new State() {
			
			@Override
			public void stateCode() {
				UIManagerAutomaton.this.activity.runOnUiThread(new Runnable(){

					public void run() {
						//set content view
						UIManagerAutomaton.this.activity.setContentView(R.layout.main_activity_layout);
						toolbarLayout = (LinearLayout) UIManagerAutomaton.this.activity.findViewById(R.id.toolbarLayout);
						inputEditText = (EditText) UIManagerAutomaton.this.activity.findViewById(R.id.inputEditText);
						voiceRecognitionButton = (ImageView) UIManagerAutomaton.this.activity.findViewById(R.id.voiceRecognitionButton);
						sendButton = (ImageView) UIManagerAutomaton.this.activity.findViewById(R.id.sendButton);
						conversationList = (ListView) UIManagerAutomaton.this.activity.findViewById(R.id.conversationList);
						
						sendButton.setOnClickListener(new OnClickListener() {
							
							public void onClick(View v) {
								MessageRouter.getInstance().send(SocietyInformation.UI_MANAGER_NAME,
										new Message(Constants.UI_NAME, Constants.USER_MESSAGE + inputEditText.getText()));
								inputEditText.setText("");
							}
						});
						
						voiceRecognitionButton.setOnClickListener(new OnClickListener() {
							
							public void onClick(View v) {
								MessageRouter.getInstance().send(SocietyInformation.UI_MANAGER_NAME,
										new Message(Constants.UI_NAME, Constants.GET_VOICE_INPUT));
							}
						});
						
						conversationListData = new ArrayList<ConversationListEntry>();
						
						conversationList.setDivider(null);
						conversationList.setDividerHeight(0);
						conversationList.setAdapter(new ConversationListAdapter(UIManagerAutomaton.this.activity, conversationListData));
						conversationList.setStackFromBottom(true);
						conversationList.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
					}
					
				});
			}
		});
		
		states.get(INITIALIZE_UI_STATE).transition = new SimpleTransition(RESIZE_UI_STATE);
		
		
		/* 1) THE UI IS RESIZED ACCORDING TO THE SCREEN SIZE */
		states.put(RESIZE_UI_STATE, new State(){

			@Override
			public void stateCode() {
				UIManagerAutomaton.this.activity.runOnUiThread(new Runnable(){

					public void run() {
						//get screen size
						DisplayMetrics dm = new DisplayMetrics();
						UIManagerAutomaton.this.activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
						
						//calculate and apply the component sizes						
						if(dm.widthPixels>dm.heightPixels){		//landscape
							toolbarLayout.getLayoutParams().width = dm.widthPixels;
							toolbarLayout.getLayoutParams().height = dm.widthPixels/8;
							voiceRecognitionButton.getLayoutParams().width = dm.widthPixels/8;
							voiceRecognitionButton.getLayoutParams().height = dm.widthPixels/8;
							inputEditText.getLayoutParams().width = dm.widthPixels*3/4;
							inputEditText.getLayoutParams().height = dm.widthPixels/8;
							sendButton.getLayoutParams().width = dm.widthPixels/8;
							sendButton.getLayoutParams().height = dm.widthPixels/8;
						} else {								//portrait
							toolbarLayout.getLayoutParams().width = dm.widthPixels;
							toolbarLayout.getLayoutParams().height = dm.heightPixels/8;
							voiceRecognitionButton.getLayoutParams().width = dm.heightPixels/8;
							voiceRecognitionButton.getLayoutParams().height = dm.heightPixels/8;
							inputEditText.getLayoutParams().width = dm.widthPixels-dm.heightPixels/4;
							inputEditText.getLayoutParams().height = dm.heightPixels/8;
							sendButton.getLayoutParams().width = dm.heightPixels/8;
							sendButton.getLayoutParams().height = dm.heightPixels/8;
						}
						toolbarLayout.requestLayout();
					}
					
				});
			}
			
		});

		/* move to the idle state */
		states.get(RESIZE_UI_STATE).transition = new SimpleTransition(IDLE_STATE);
		
		
		/* 2) IDLE STATE: THE AUTOMATON WAITS FOR MESSAGES HERE AND INTERPRETS THEM */
		states.put(IDLE_STATE, new NullState());
		
		states.get(IDLE_STATE).transition = new Transition(){

			@Override
			public String transitionCode() {
				currentMessage = messageList.remove();
				if(currentMessage.data.equals(Constants.SCREEN_ORIENTATION_CHANGED)){
					return RESIZE_UI_STATE;
				} else if(currentMessage.data.equals(Constants.GET_VOICE_INPUT)){
					return REQUEST_VOICE_INPUT_STATE;
				} else if(currentMessage.data.toString().startsWith(Constants.ERROR_RESPONSE)){
					return DISPLAY_ERROR_STATE;
				} else if(currentMessage.data.toString().startsWith(Constants.USER_MESSAGE)){
					return PROCESS_USER_MESSAGE;
				} else if(currentMessage.data.toString().equals(Constants.SEND_AUTOMATON_REQUEST)){
					return PROCESS_AUTOMATON_SEND_REQUEST;
				} else if(currentMessage.data.toString().equals(Constants.REQUEST_AUTOMATON_REQUEST)){
					return PROCESS_AUTOMATON_REQUEST_REQUEST;
				} else if(currentMessage.from != null){
					return PROCESS_OTHER_MESSAGE;
				} else {
					return IDLE_STATE;
				}
			}
			
		};
		
		
		/* 3) THE UI GETS THE VOICE INPUT */
		states.put(REQUEST_VOICE_INPUT_STATE, new State(){
			@Override
			public void stateCode() {
				UIManagerAutomaton.this.activity.runOnUiThread(new Runnable() {
					
					public void run() {
						//verify if phone is capable of voice recognition
						PackageManager pm = UIManagerAutomaton.this.activity.getPackageManager();
						List<ResolveInfo> activities = pm.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
						if (activities.size() == 0) {
							addMessage(new Message(SocietyInformation.UI_MANAGER_NAME,
													Constants.ERROR_RESPONSE + Constants.ERROR_VOICE_RECOGNITION_UNAVAILABLE));
							return;
						}
						
						//start the activity that performs voice recognition
						Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
						intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, 
								UIManagerAutomaton.this.activity.getClass().getPackage().getName());
						//intent.putExtra(RecognizerIntent.EXTRA_PROMPT, /*text*/);
						
						// 1.LANGUAGE_MODEL_WEB_SEARCH : For short phrases
						// 2.LANGUAGE_MODEL_FREE_FORM : If not sure about the words or phrases
						intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
						
						intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1);
						// Start the Voice recognizer activity for the result.
						UIManagerAutomaton.this.activity.startActivityForResult(intent, Constants.VOICE_RECOGNITION_REQUEST_CODE);
					}
				});
			}
		});
		
		states.get(REQUEST_VOICE_INPUT_STATE).transition = new Transition(){

			@Override
			public String transitionCode() {
				while(true){
					currentMessage = messageList.remove();
					if(currentMessage.from.equals(Constants.UI_NAME) && currentMessage.data instanceof Object[]){
						Object[] result = (Object[]) currentMessage.data;
						if(result.length>0 && result[0].equals(Constants.ACTIVITY_RESULT)){
							return PROCESS_VOICE_INPUT_RESPONSE_STATE;
						}
					}
				}
			}
			
		};
		
		
		/* 4) THE AUTOMATON PROCESSES THE VOICE INPUT AND SENDS IT TO THE REQUESTER (OR PUTS IT TO THE INPUT EDIT TEXT) */
		states.put(PROCESS_VOICE_INPUT_RESPONSE_STATE, new State(){
			@Override
			public void stateCode() {
				UIManagerAutomaton.this.activity.runOnUiThread(new Runnable() {
					
					public void run() {
						String voiceInputText = "";
						
						Object[] result = (Object[]) currentMessage.data;
						int requestCode = (Integer) result[1];
						int resultCode = (Integer) result[2];
						Intent data = (Intent) result[3];
						
						if (requestCode == Constants.VOICE_RECOGNITION_REQUEST_CODE){
							if (resultCode == Activity.RESULT_OK) {
								ArrayList<String> textMatchList = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
								if (!textMatchList.isEmpty()) {
									voiceInputText = textMatchList.get(0);
								}
								
							} else if (resultCode == RecognizerIntent.RESULT_AUDIO_ERROR) {
								addMessage(new Message(SocietyInformation.UI_MANAGER_NAME,
										Constants.ERROR_RESPONSE + Constants.ERROR_AUDIO));
								
							} else if (resultCode == RecognizerIntent.RESULT_CLIENT_ERROR) {
								addMessage(new Message(SocietyInformation.UI_MANAGER_NAME,
										Constants.ERROR_RESPONSE + Constants.ERROR_CLIENT));
								
							} else if (resultCode == RecognizerIntent.RESULT_NETWORK_ERROR) {
								addMessage(new Message(SocietyInformation.UI_MANAGER_NAME,
										Constants.ERROR_RESPONSE + Constants.ERROR_NETWORK));
								
							} else if (resultCode == RecognizerIntent.RESULT_NO_MATCH) {
								addMessage(new Message(SocietyInformation.UI_MANAGER_NAME,
										Constants.ERROR_RESPONSE + Constants.ERROR_NO_MATCHES));
								
							} else if (resultCode == RecognizerIntent.RESULT_SERVER_ERROR) {
								addMessage(new Message(SocietyInformation.UI_MANAGER_NAME,
										Constants.ERROR_RESPONSE + Constants.ERROR_SERVER));
							}
						}
						
						if(currentMessage.from.equals(Constants.UI_NAME)){		//insert text in input edit text
							inputEditText.setText(voiceInputText);
						} else {											//or send it to the requester
							MessageRouter.getInstance().send(currentMessage.from, 
									new Message(SocietyInformation.UI_MANAGER_NAME, Constants.VOICE_INPUT_RESPONSE + voiceInputText));
						}
						
						//resize components in case the screen orientation changed during the previous activity
						addMessage(new Message(SocietyInformation.UI_MANAGER_NAME, Constants.SCREEN_ORIENTATION_CHANGED));
					}
				});
			}
		});
		
		states.get(PROCESS_VOICE_INPUT_RESPONSE_STATE).transition = new SimpleTransition(IDLE_STATE);
		
		
		/* 5) DISPLAYS AN ERROR MESSAGE */
		states.put(DISPLAY_ERROR_STATE, new State(){
			@Override
			public void stateCode() {
				UIManagerAutomaton.this.activity.runOnUiThread(new Runnable() {
					
					public void run() {
						conversationListData.add(new ConversationListEntry(android.R.drawable.sym_action_chat,
								activity.getResources().getColor(R.color.red),
								currentMessage.data.toString().substring(Constants.ERROR_RESPONSE.length())));
						UIManagerAutomaton.this.activity.runOnUiThread(new Runnable() {
							
							public void run() {
								((ConversationListAdapter)conversationList.getAdapter()).notifyDataSetChanged();
							}
						});
					}
				});
			}
		});
		
		states.get(DISPLAY_ERROR_STATE).transition = new SimpleTransition(IDLE_STATE);
		
		/* 6) PROCESSES A USER MESSAGE */
		states.put(PROCESS_USER_MESSAGE, new State(){
			@Override
			public void stateCode() {
				final String message = currentMessage.data.toString().substring(Constants.USER_MESSAGE.length());
				
				UIManagerAutomaton.this.activity.runOnUiThread(new Runnable() {
					
					public void run() {
						
						conversationListData.add(new ConversationListEntry(android.R.drawable.sym_action_chat,
								activity.getResources().getColor(R.color.black), message));
						((ConversationListAdapter)conversationList.getAdapter()).notifyDataSetChanged();
						synchronized(message){
							message.notify();
						}
					}
				});
				synchronized(message){
					try {
						message.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				MessageRouter.getInstance().send(Constants.NEWS_FETCHER_NAME, new Message("UIAutomaton",
						currentMessage.data.toString().substring(Constants.USER_MESSAGE.length())));
			}
			
		});
		
		states.get(PROCESS_USER_MESSAGE).transition = new SimpleTransition(IDLE_STATE);
		
		/* *) PROCESSES A TEST MESSAGE */
		states.put(PROCESS_OTHER_MESSAGE, new State(){
			@Override
			public void stateCode() {
				String auxMessage = currentMessage.data.toString();
				int auxColor = activity.getResources().getColor(R.color.black);
				if(auxMessage.startsWith("red:")){
					auxMessage = auxMessage.substring("red:".length());
					auxColor = activity.getResources().getColor(R.color.red);
				} else if(auxMessage.startsWith("green:")){
					auxMessage = auxMessage.substring("green:".length());
					auxColor = activity.getResources().getColor(R.color.green);
				} else if(auxMessage.startsWith("gray:")){
					auxMessage = auxMessage.substring("gray:".length());
					auxColor = activity.getResources().getColor(R.color.gray);
				} else if(auxMessage.startsWith("black:")){
					auxMessage = auxMessage.substring("black:".length());
				}
				
				final String message = auxMessage;
				final int color = auxColor;
				UIManagerAutomaton.this.activity.runOnUiThread(new Runnable() {
					
					public void run() {
						conversationListData.add(new ConversationListEntry(android.R.drawable.sym_action_email, color, message));
						((ConversationListAdapter)conversationList.getAdapter()).notifyDataSetChanged();
						synchronized(message){
							message.notify();
						}
					}
				});
				synchronized(message){
					try {
						message.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		
		states.get(PROCESS_OTHER_MESSAGE).transition = new SimpleTransition(IDLE_STATE);
		
		/* **) PROCESSES AN AUTOMATON SEND REQUEST */
		states.put(PROCESS_AUTOMATON_SEND_REQUEST, new State(){
			@Override
			public void stateCode() {
				try {
					((SocietyActivity)activity).getSocietyManager()
						.sendXMLAutomatonToRemoteApplication(Constants.NEWS_FETCHER_NAME);
					
					UIManagerAutomaton.this.activity.runOnUiThread(new Runnable() {
						
						public void run() {
							Toast.makeText(UIManagerAutomaton.this.activity,
									"The request has been registered", Toast.LENGTH_SHORT).show();
						}
					});
				} catch (final Exception e) {
					UIManagerAutomaton.this.activity.runOnUiThread(new Runnable() {

						public void run() {
							Toast.makeText(
									UIManagerAutomaton.this.activity,
									e.getMessage(), Toast.LENGTH_SHORT)
									.show();
						}
					});
				}
			}
		});
		
		states.get(PROCESS_AUTOMATON_SEND_REQUEST).transition = new SimpleTransition(IDLE_STATE);
		
		/* ***) PROCESSES AN AUTOMATON REQUEST REQUEST */
		states.put(PROCESS_AUTOMATON_REQUEST_REQUEST, new State(){
			@Override
			public void stateCode() {
				try {
					((SocietyActivity)activity).getSocietyManager()
						.requestXMLAutomatonFromRemoteApplication(Constants.NEWS_FETCHER_NAME);
					
					UIManagerAutomaton.this.activity.runOnUiThread(new Runnable() {
						
						public void run() {
							Toast.makeText(UIManagerAutomaton.this.activity,
									"The request has been registered", Toast.LENGTH_SHORT).show();
						}
					});
				} catch (final Exception e) {
					UIManagerAutomaton.this.activity.runOnUiThread(new Runnable() {

						public void run() {
							Toast.makeText(
									UIManagerAutomaton.this.activity,
									e.getMessage(), Toast.LENGTH_SHORT)
									.show();
						}
					});
				}
			}
		});
		
		states.get(PROCESS_AUTOMATON_REQUEST_REQUEST).transition = new SimpleTransition(IDLE_STATE);
	}

}
